#include "iuCommandPing.hpp"
#include <cstdio>
#include <cstring>
#include <unistd.h>
#include "iuApplication.hpp"
#include "iuPacketICMP.hpp"
#include "iuPacketIP.hpp"
#include "iuModuleIP.hpp"
#include "iuIPAddress.hpp"

static void *pingThread(void *data);

/**
 * @brief Recupere le nom de la commande
 * @return Renvoie le nom de la commande (ici, "ping")
 */
const char *iuCommandPing::getName() const
{
   static const char *name = "ping";

   return name;
}

/**
 * @brief Execute la commande
 * @param numArgs le nombre d'arguments de la commande
 * @param args les arguments de la commande (args[0] est le nom de la commande)
 * @param app l'application executant la commande
 */
void iuCommandPing::execute(int numArgs, char **args, iuApplication &app)
{
   iuIPAddress dest;
   int count, ttl, size;
   bool dontFrag;
   bool ipFound = false;

   count = 1;
   ttl = 255;
   dontFrag = false;
   size = 0;

   //Envoi du requete de ping
   if (numArgs >= 2)
   {
      //Lit les arguments de la commande
      for (int i = 1; i < numArgs; i++)
      {
         //Lit le nombre de requetes a envoyer
         if (strcmp(args[i], "-c") == 0 && i < numArgs - 1)
         {
            i++;
            if (sscanf(args[i], "%d", &count) != 1)
            {
               printf("ping: Invalid argument: the 'count' argument should be a number\n");
               return;
            }
         }

         //Lit le TTL des requetes a envoyer
         else if (strcmp(args[i], "-t") == 0 && i < numArgs - 1)
         {
            i++;
            if (sscanf(args[i], "%d", &ttl) != 1)
            {
               printf("ping: Invalid argument: the 'TTL' argument should be a number\n");
               return;
            }
         }

         //Lit l'option dont-frag des requetes a envoyer
         else if (strcmp(args[i], "-M") == 0 && i < numArgs - 1)
         {
            i++;
            if (strcmp(args[i], "do") == 0)
               dontFrag = true;
            else if (strcmp(args[i], "dont") == 0)
               dontFrag = false;
            else
            {
               printf("ping: Invalid argument: the 'hint' argument should be 'do' or 'dont'\n");
               return;
            }
         }

         //Lit la taille des requetes a envoyer
         else if (strcmp(args[i], "-S") == 0 && i < numArgs - 1)
         {
            i++;
            if (sscanf(args[i], "%d", &size) != 1)
            {
               printf("ping: Invalid argument: the 'size' argument should be a number\n");
               return;
            }
         }

         //Lit l'adresse IP de destination
         else if (!dest.fromString(args[i]))
         {
            printf("ping: Invalid destination address\n");
            return;
         }
         else
            ipFound = true;
      }

      if (!ipFound)
      {
         printf("Incorrect usage:\n");
         printf("  ping: Destination IP not found\n");
         return;
      }

      //Creer les paquets a envoyer
      m_requestICMP.setType(ICMP_ECHO_REQUEST);
      m_requestICMP.setSize(size);

      m_requestIP.setDestination(dest);
      m_requestIP.setTTL(ttl);
      m_requestIP.setFlags(dontFrag, false);
      m_requestIP.setProtocol(0x1);
      m_requestIP.setData(&m_requestICMP);

      m_count = count;
      m_app = &app;
      
      //Creer le thread charge d'envoyer les paquets
      pthread_create(&m_thread, NULL, pingThread, this);
   }

   //Usage incorrect
   else
   {
      printf("Incorrect usage:\n");
      printf("  ping [-c count] [-t TTL] [-M do|dont] [-S size] dest-ip\n");
   }
}

/**
 * @brief Envoie les requetes ICMP
 */
void iuCommandPing::sendRequests()
{
   for (int i = 0; i < m_count; i++)
   {
      m_requestIP.setId(iuPacketIP::getNextId());
      printf("Sending Echo-Request %d/%d to %s...\n", i + 1, m_count, m_requestIP.getDestination().toString());
      m_app->getModuleIP().sendPacket(m_requestIP);
      usleep(10000);
   }
   
   m_requestIP.setData(NULL);
}

/**
 * @brief Thread effectuant les envois de requetes ICMP
 */
static void *pingThread(void *data)
{
   iuCommandPing *cmd;

   if ((cmd = (iuCommandPing *)data))
      cmd->sendRequests();

   return NULL;
}
